package fybug.nulll.pdstream.IO.File;
import com.sun.istack.internal.NotNull;
import com.sun.istack.internal.Nullable;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

import fybug.nulll.pdfunctionlibrary.Processing.Err.Stop;
import fybug.nulll.pdfunctionlibrary.Util.Processing.Arrarys;
import fybug.nulll.pdstream.IO.Error.Path.ParamNoFile;
import fybug.nulll.pdstream.IO.NI_O;

import static fybug.nulll.pdfunctionlibrary.Util.Processing.Arrarys.conversionArrayType;
import static fybug.nulll.pdstream.IO.File.DirOper.emptyFile;
/**
 * <h2>文件操作器.</h2>
 * <pre>
 * 提供清空文件
 * 清除空文件
 * 批量移动文件等操作
 *
 * <b>除去批量操作方法，单独的操作方法通常为静态方法</b>
 * </pre>
 *
 * @author fybug
 * @version 0.0.5
 * @see AbstractFileOper
 * @see ParamNoFile
 * @since JDK 1.8
 */
@SuppressWarnings( "All" )
public final
class FilerOper extends AbstractFileOper {
    /** <p>构造一个文件操作器并绑定文件组.</p> */
    public
    FilerOper(@Nullable final File... files) { super(files); }

    /** <p>构造一个文件操作器并绑定文件组.</p> */
    public
    FilerOper(@Nullable final String... s) {super(s);}

    /** <p>获取绑定的文件的读取流.</p> */
    @NotNull
    public
    FileInputStream[] toInputStream() {
        @NotNull File[] files;
        try {
            files = file();
        } catch ( Stop E ) {
            return new FileInputStream[0];
        }
        @NotNull FileInputStream fileInputStream[] = new FileInputStream[files.length];
        int mark = 0; // 当前放入位置
        try {
            @Nullable File file;
            synchronized ( files ){
                for ( int i = 0; i < files.length; i++ ){
                    /* 转化 */
                    file = files[i];
                    if (file == null || !file.isFile())
                        continue;
                    fileInputStream[mark] = new FileInputStream(file);
                    mark++;
                }
            }
        } catch ( FileNotFoundException e ) {
        }
        return Arrarys.trim(fileInputStream);
    }

    /** <p>获取绑定的文件的写入流.</p> */
    @NotNull
    public
    FileOutputStream[] toOutStream() {
        @NotNull File[] files;
        try {
            files = file();
        } catch ( Stop E ) {
            return new FileOutputStream[0];
        }
        @NotNull FileOutputStream fileInputStream[] = new FileOutputStream[files.length];
        int mark = 0; // 当前放入位置
        try {
            @Nullable File file;
            synchronized ( files ){
                for ( int i = 0; i < files.length; i++ ){
                    /* 转化 */
                    file = files[i];
                    if (file == null || !file.isFile())
                        continue;
                    fileInputStream[mark] = new FileOutputStream(file);
                    mark++;
                }
            }
        } catch ( FileNotFoundException e ) {
        }
        return Arrarys.trim(fileInputStream);
    }

    /*
     * File
     */

    /**
     * <p>重绑定文件组.</p>
     * <p>会过滤出路径组中的文件</p>
     */
    @NotNull
    @Override
    public
    FilerOper binFile(@Nullable final File... files) {
        // 过滤出文件
        return (FilerOper) super.binFile(conversionArrayType(files, (file) -> {
            if (file.isDirectory() || !file.isFile())
                return null;
            return file;
        }, File.class));
    }

    /**
     * <p>增加绑定的文件组</p>
     *
     * @see #binFile(File...)
     */
    @Override
    @NotNull
    public
    FilerOper appendFile(@Nullable final File... files) {
        // 过滤出文件
        return (FilerOper) super.appendFile(conversionArrayType(files, (file) -> {
            if (file.isDirectory() || !file.isFile())
                return null;
            return file;
        }, File.class));
    }

    /*
     * Move
     */

    /**
     * <p>移动绑定的 <b>文件</b> 到指定的 <b>文件夹</b> 中.</p>
     * <pre>
     * 保持文件的命名
     * <b>并更新内部路径组为移动后的路径组</b>
     *
     * 如绑定的源文件为：{@code File("/data/A"),File("/data/S");}
     * 移动到{@code File("/data/B");}
     * 则移动后的路径为：{@code File("/data/B/A"),File("/data/B/S");}
     * </pre>
     *
     * @param to 移动到该文件夹下
     */
    @NotNull
    public
    FilerOper mv(@NotNull final File to) throws IOException {
        // 检查参数
        noUserFile(to);
        to.mkdirs();
        @NotNull File[] files;
        try {
            files = file();
        } catch ( Stop ignored ) {
            return this;
        }
        @NotNull File from;
        synchronized ( files ){
            for ( int i = 0; i < files.length; i++ ){
                from = files[i]; // 减少寻址
                // 使用零复制
                NI_O.zerCopy(from, files[i] = new File(to, from.getName()));
                // 删除源文件
                from.delete();
            }
        }
        return this;
    }

    /**
     * <p>移动并 <b>重命名文件</b>.</p>
     * <pre>
     * 将文件使用 <b>零复制</b> 移动到指定位置
     * 要移动到的位置 <b>不可以是已经有文件夹覆盖</b> 的位置
     * </pre>
     *
     * @param from 将该文件移动
     * @param to   移动为该路径
     *
     * @return 移动后的路径
     */
    public static
    File mv(@NotNull final File from, @NotNull final File to) throws IOException {
        // 检查参数
        checkFile(from);
        noUserDir(to);
        // 创建父文件夹
        to.getParentFile().mkdirs();
        // 使用零复制
        NI_O.zerCopy(from, to);
        // 删除源文件
        from.delete();
        return to;
    }

    /*
     * Clean
     */

    /**
     * <p>删除绑定路径中的 <b>空文件</b> .</p>
     * <p>删除 <b>源文件</b> 并从列表中移除</p>
     */
    @NotNull
    public
    FilerOper cleanEmpty() {
        @NotNull File[] files;
        try {
            files = file();
        } catch ( Stop e ) {
            return this;
        }
        @NotNull File file;
        synchronized ( files ){
            for ( int i = 0; i < files.length; i++ )
                if ((file = files[i]).length() == 0) {
                    file.delete();
                    files[i] = null;
                }
            file = null;
            binFile(files);
        }
        return this;
    }

    /** <p>删除该 <b>文件夹</b> 下的 <b>空文件</b> .</p> */
    public static
    File cleanEmpty(@NotNull final File f) {
        @NotNull File[] files = emptyFile(f); // 获取的空文件列表
        for ( int i = 0; i < files.length; i++ )
            /* 清除空文件 */
            files[i].delete();
        return f;
    }

    /**
     * <p>清空绑定的文件.</p>
     * <pre>
     * 会被调整为 <b>空文件</b>
     * </pre>
     *
     * @see #cleanFile(File)
     */
    @NotNull
    public
    FilerOper cleanFile() {
        @NotNull File[] files;
        try {
            files = file();
        } catch ( Stop e ) {
            return this;
        }
        synchronized ( files ){
            for ( int i = 0; i < files.length; i++ )
                cleanFile(files[i]);
        }
        return this;
    }

    /**
     * <p>清空文件.</p>
     * <pre>
     * 使用 {@link FileChannel#truncate(long)} 调整文件大小为{@code 0}
     * 在操作期间会 <b>获取文件锁</b>
     * 如果 <b>该文件不存在</b> ,则会 <b>生成一个空文件</b>
     * </pre>
     *
     * @param f 要清空的文件
     */
    public static
    File cleanFile(@NotNull final File f) {
        noUserDir(f);
        // 生成父路径
        f.getParentFile().mkdirs();
        @Nullable FileChannel fileChannel = null;
        @Nullable FileOutputStream outputStream = null;
        @Nullable FileLock fileLock = null;
        try {
            /* 使用文件输出流获取文件通道 */
            outputStream = new FileOutputStream(f, false);
            fileChannel = outputStream.getChannel();
            do {
                fileLock = fileChannel.tryLock();
            }
            while( fileLock == null );
            if (fileLock.isValid()) {
                // 裁剪为 0 字节
                fileChannel.truncate(0);
                // 同步到物理设备
                fileChannel.force(true);
            }
        } catch ( Exception ignored ) {
        } finally {
            try {
                if (fileLock != null)
                    fileLock.close();
            } catch ( IOException ignored ) {
            }
            try {
                if (fileChannel != null)
                    fileChannel.close();
            } catch ( IOException ignored ) {
            }
            try {
                if (outputStream != null)
                    outputStream.close();
            } catch ( IOException ignored ) {
            }
        }
        return f;
    }

    /**
     * <p>删除绑定的文件.</p>
     * <pre>
     * 删除源文件并清空绑定
     * </pre>
     */
    @NotNull
    public
    FilerOper dele() {
        @NotNull final File[] fs;
        try {
            fs = file();
        } catch ( Stop e ) {
            return this;
        }
        for ( int i = 0; i < fs.length; i++ )
            fs[i].delete();
        setValue(null);
        return this;
    }
}
